package com.gitee.wsl.common.ui.draw.container

import androidx.compose.ui.graphics.Color
import com.gitee.wsl.common.ui.base.HasSize
import com.gitee.wsl.common.ui.base.toColorOrBrush
import com.gitee.wsl.common.ui.draw.api.HasChildren
import com.gitee.wsl.common.ui.draw.style.NodeStyle
import com.gitee.wsl.common.ui.draw.style.NodeStyleImpl
import com.gitee.wsl.common.ui.input.Disposable
import com.gitee.wsl.common.ui.input.event.EventPropagation
import com.gitee.wsl.common.ui.input.event.KEvent
import com.gitee.wsl.common.ui.input.KEventHandle
import com.gitee.wsl.common.ui.input.KEventListener
import com.gitee.wsl.time.timer.Timer

/**
 * Viz is the base element of a visualization.
 *
 * It is associated with a renderer which is used to perform the rendering depending on
 * the current platform.
 *
 * It has at least one layer (the activeLayer). Layers provide a way of managing what is
 * drawn on the background and what is drawn on frontend. The rendering process start with
 * the layer with the lower index.
 *
 * Viz respects the `HasChildren` interface. It is possible to directly invoke some creation
 * function on its context. The created element are then added to the active layer. It provides
 * a very easy way to start a visualization.
 *
 * Viz width and height can be seen as the target size of the visualization on a standard
 * screen (no hi resolution) for a web rendering. These sizes will then be used during the
 * rendering process on each platform to adapt the visualization to the target device, taking
 * in account the resolution of the screen, and the configuration of the viz.
 */
 class Viz(
    val layer: Layer = Layer(),
    private val style: NodeStyle = NodeStyleImpl()
) : NodeStyle by style,
    HasChildren by layer,
    HasSize {

    var activeLayer: Layer

    val layers: MutableList<Layer> = mutableListOf(layer)

    init {
        activeLayer = layer

        layer.parent = this

        textColor = Color.Black.toColorOrBrush()
    }

     val config: VizConfig = VizConfig()

    override var width: Float = 100.0f
    override var height: Float = 100.0f

    private var resizeBehavior: ((Double, Double) -> Unit)? = null

    private val eventListeners = mutableListOf<KEventHandle<*>>()

     var renderer: VizRenderer? = null
        set(newValue) {
            val oldValue = field
            field = newValue
            eventListeners.forEach {
                oldValue?.removeEventHandle(it)
                newValue?.addEventHandle(it)
            }
        }

    /**
     * This is the common function call to add all type of events listener to a Viz.
     *
     * 	val handle = on(KPointerClick) { evt -> println("Pointer click:: ${evt.pos}")}
     *
     * 	A disposable handle is returned, allowing the caller to remove the listener.
     */
     fun <T : KEvent> on(eventListener: KEventListener<T>, listener: (T) -> EventPropagation): Disposable {
        val eventHandle = KEventHandle(eventListener, listener) {
            eventListeners.remove(it) //todo why?

            renderer?.apply {
                removeEventHandle(it)
            }
        }
        eventListeners.add(eventHandle)
        renderer?.addEventHandle(eventHandle)

        return eventHandle
    }

     fun render() {
        renderer?.render()
    }

     fun startAnimations() {
        renderer?.startAnimations()
    }

     fun stopAnimations() {
        renderer?.stopAnimations()
    }

    private val animationTimers = mutableListOf<Timer.(Double) -> Unit>()

    /**
     * Add an animation timer. The given block is an extension function on a Timer.
     * It will be executed inside a timer with the elapsed time in ms as a parameter.
     * It is possible to stop the timer from the block by calling `stop()` function
     * from the block.
     */
    fun animation(block: Timer.(Double) -> Unit) {
        animationTimers.add(block)
    }


     fun onResize(block: (newWidth: Double, newHeight: Double) -> Unit) {
        resizeBehavior = block
    }

     fun resize(newWidth: Double, newHeight: Double) {
        resizeBehavior?.invoke(newWidth, newHeight)
    }

     fun layer(): Layer {
        val layer = Layer().also { it.parent = this }
        layers.add(layer)
        activeLayer = layer
        return layer
    }

    /**
     * Used to keep a strong reference to the listener, so it doesn't get garbage collected
     * until this object is.
     */
    internal lateinit var pixelRatioChangeListener: () -> Unit
}

 class VizConfig {
     var autoUpdate: Boolean = true
}
